home *** CD-ROM | disk | FTP | other *** search
- ////////////////////////////////////////////////////////////////////////////
- //
- // XMODEM.C - Written by Mike Sax for Dr. Dobb's Journal
- //
- // This file contains two public functions:
- // BOOL UploadXModem(HWND hParent, int gnPortID, LPSTR lpFilename);
- // BOOL DownloadXModem(HWND hParent, int gnPortID, LPSTR lpFilename);
- //
- // These functions will transfer the file specified by lpFilename on the
- // Windows comm. port specified by gnPortID. The hParent parameter is the
- // handle of the window that will be the parent of the XModem status dialog.
- //
- // To include these functions in your own program, you should include the
- // following files with your program: XMODEM.C COMM.C XMODEM.RC. No
- // additional functions or variables are required.
- //
- ////////////////////////////////////////////////////////////////////////////
-
- #define dprintf(s) MessageBox(GetFocus(), s, "Debug Message", MB_OK)
-
- // xmodem.c
- #define USECOMM 1 // for 3.1 windows.h
- #include "windows.h"
- #include "wincom.h"
- #include "comm.h"
- #include <stdio.h>
- #include <conio.h>
- #include <stdlib.h>
- #include <memory.h>
-
- #define RETRIES 12
- #define CRCTRIES 2
- #define PADCHAR 0x1a
- #define SOH 1
- #define EOT 4
- #define ACK 6
- #define NAK 0x15
- #define CAN 0x18
- #define CRC 'C'
-
- // Local data
- static int gcTries; // retry counter
- static char gachBuffer[130]; // I/O buffer
- static int ghStatusDlg; // Handle of the transfer status dialog
- static BOOL gbUserCanceled; // Did the user press cancel?
- static int gnPortID; // ID of the current comm. port
- static FARPROC glpDlgProc; // ProcInstance of status dialog
- static BOOL gbParentEnabled; // Parent of status dialog enabled?
- static int gbTimeOut; // Time out char when reading?
- static DCB gDCB; // Comm. status that we save
-
- // Prototypes:
- static void ReceiveError(int, int);
- static void Sleep(int);
- static void Status(char *);
- static void TestWordLen(void);
- HANDLE GetCurrentInstance(void);
- static BOOL TimeOut(void);
- WORD CalculateCRC(char *pchBuffer, int nLen);
- static BOOL CreateTransferDialog(HWND hParent, char *szTitle, char *szFilename);
- void DestroyTransferDialog(void);
- BOOL _export _far PASCAL TransferDlgProc(HWND hDlg, WORD wMessage, WORD wParam, long lParam);
- static void DoEvents(void);
- static void ModifyCommState(void);
- static void RestoreCommState(void);
-
- // Error messages
- static char *aszError[] =
- {
- "Timed Out",
- "Invalid SOH",
- "Invalid Block #",
- "Invalid checksum/crc"
- };
-
- enum
- {
- errTIMEOUT,
- errINVALIDSOH,
- errINVALIDBLOCK,
- errINVALIDCHECKSUM
- };
-
- // Give control to other windows
- static void DoEvents(void)
- {
- MSG msg;
-
- while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
- {
- if (msg.message == WM_QUIT)
- { // Our application has to quit: simulate user pressed cancel
- PostQuitMessage(msg.wParam);
- gbUserCanceled = TRUE;
- return;
- }
- if (!IsDialogMessage(ghStatusDlg, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- }
-
- // Classic "pause" function for Windows
- static void Sleep(int nTicks)
- {
- DWORD dwEnd = GetTickCount() + nTicks * 55;
-
- while ((dwEnd > GetTickCount()) && !gbUserCanceled)
- DoEvents();
- }
-
- // Wait x seconds for char in the com. port, return -1 if none, char otherwise
- static int ComReadCharTimeOut(int nPortID, int nSeconds)
- {
- DWORD dwEnd = GetTickCount() + nSeconds * 1000l;
-
- gbTimeOut = FALSE;
- while ((dwEnd > GetTickCount()) && !gbUserCanceled)
- {
- if (CharsWaitingToBeRead(nPortID))
- return ComReadChar(nPortID);
- DoEvents();
- }
- gbTimeOut = TRUE;
- return -1;
- }
-
- // Tansfer status dialog proc.
- BOOL _export _far PASCAL TransferDlgProc(HWND hDlg, WORD wMessage, WORD wParam,
- long lParam)
- {
- switch(wMessage)
- {
- case WM_INITDIALOG:
- gbUserCanceled = FALSE;
- break;
- case WM_COMMAND:
- if (wParam != IDCANCEL)
- return FALSE; // Let dialog perform default action
- case WM_CLOSE: // fall thru!
- gbUserCanceled = TRUE;
- break;
- default:
- return FALSE;
- }
- return TRUE;
- }
-
- // Returns the instance of the current task
- HANDLE GetCurrentInstance(void)
- {
- _asm push ss
- _asm call GlobalHandle
- // return value in ax
- }
-
- // Create transfer dialog box. Return TRUE if success, FALSE otherwise
- static BOOL CreateTransferDialog(HWND hParent, char *szTitle, char *szFilename)
- {
- HANDLE hInstance = GetCurrentInstance();
-
- glpDlgProc = MakeProcInstance((FARPROC)TransferDlgProc, hInstance);
- if (NULL == glpDlgProc)
- return FALSE;
- ghStatusDlg = CreateDialog(hInstance, "XMDMSTATUS", hParent, glpDlgProc);
- if (ghStatusDlg)
- {
- DoEvents();
- SetWindowText(ghStatusDlg, szTitle);
- SetDlgItemText(ghStatusDlg, IDD_FILENAME, szFilename);
- ShowWindow(ghStatusDlg, SW_SHOW);
- gbParentEnabled = !EnableWindow(hParent, FALSE);
- ModifyCommState();
- }
- else
- FreeProcInstance(glpDlgProc);
- return ghStatusDlg;
- }
-
- // Destroys the transfer status dialog box
- void DestroyTransferDialog(void)
- {
- if (ghStatusDlg)
- {
- EnableWindow(GetParent(ghStatusDlg), gbParentEnabled);
- DestroyWindow(ghStatusDlg);
- FreeProcInstance(glpDlgProc);
- ghStatusDlg = NULL;
- RestoreCommState();
- }
- }
-
- // Saves current comm. port settings and sets XOn/XOff off, N-8-1
- static void ModifyCommState(void)
- {
- DCB dcb;
-
- GetCommState(gnPortID, &gDCB);
- dcb = gDCB;
- dcb.fOutX = dcb.fInX = (BYTE)FALSE;
- dcb.ByteSize = (BYTE)8;
- dcb.Parity = NOPARITY;
- dcb.StopBits = (BYTE)ONESTOPBIT;
- SetCommState(&dcb);
- }
-
- static void RestoreCommState(void)
- {
- SetCommState(&gDCB);
- }
-
- // Upload a file using the XModem protocol return TRUE if success
- // hParent: the handle of the window that should be the parent of
- // the transfer status dialog box (shouldn't be NULL).
- // gnPortID: the port ID of the port which is used for the transfer
- // szFilename: the name of the file to be transfered
- BOOL UploadXModem(HWND hParent, int gnPortID, char *szFilename)
- {
- int i, nCheckSum, bEOF = FALSE, chAnswer = 0, nLength, chCRCOut = 0;
- WORD wCRC;
- char nBlock = 1;
- BOOL bResult;
- int hFile;
-
- hFile = _lopen(szFilename, OF_READ);
- if ((gnPortID < 0) || (hFile < 1))
- return FALSE; // Port is not open or file not found
- if (!CreateTransferDialog(hParent, "XMODEM Upload (Checksum)", szFilename))
- {
- _lclose(hFile);
- return FALSE; // Couldn't create dialog --> failure
- }
- gcTries = 0;
- while (gcTries++ < RETRIES && chCRCOut != NAK && chCRCOut != CRC)
- chCRCOut = ComReadCharTimeOut(gnPortID, 6);
- gcTries = 0;
- if (chCRCOut == CRC)
- SetWindowText(ghStatusDlg, " XMODEM Upload (CRC) ");
- else if (chCRCOut != NAK) // Time out or tried 10 times without succes
- gcTries = RETRIES;
- while (gcTries < RETRIES && !bEOF && chAnswer != CAN)
- {
- // read the next data block
- memset(gachBuffer, 128, PADCHAR);
- if ((nLength = _lread(hFile, gachBuffer, 128)) != 128)
- bEOF = TRUE;
- if (nLength == 0)
- break;
- SetDlgItemInt(ghStatusDlg, IDD_BLOCK, nBlock, FALSE);
- if (gbUserCanceled)
- {
- ComWriteChar(gnPortID, CAN);
- ComWriteChar(gnPortID, CAN);
- break;
- }
- ComWriteChar(gnPortID, SOH); // SOH
- ComWriteChar(gnPortID, nBlock); // block number
- ComWriteChar(gnPortID, ~nBlock); // 1s complement
- nCheckSum = 0;
- // send the data block
- for (i = 0; i < 128; i++)
- {
- ComWriteChar(gnPortID, gachBuffer[i]);
- nCheckSum += gachBuffer[i]; // checksum calculation
- }
- // Send error-correcting value (nCheckSum or crc)
- if (chCRCOut == NAK)
- ComWriteChar(gnPortID, nCheckSum & 255);
- else
- {
- wCRC = CalculateCRC(gachBuffer, 130);
- ComWriteChar(gnPortID, (wCRC >> 8) & 255);
- ComWriteChar(gnPortID, wCRC & 255);
- }
- // read ACK, NAK, or CAN from receiver
- chAnswer = ComReadCharTimeOut(gnPortID, 10);
- if (chAnswer == ACK)
- {
- nBlock++;
- gcTries = 0;
- SetDlgItemInt(ghStatusDlg, IDD_ERRORS, 0, FALSE);
- SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, "");
- }
- else
- {
- bEOF = FALSE;
- SetDlgItemInt(ghStatusDlg, IDD_ERRORS, ++gcTries, FALSE);
- SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, chAnswer == NAK ?
- "Invalid CRC" : "Time out");
- // Position to previous block
- if (_llseek(hFile, -128l, 1) == -1)
- _llseek(hFile, 0L, 0);
- }
- }
- if (bEOF)
- {
- ComWriteChar(gnPortID, EOT); // send the EOT
- ComReadCharTimeOut(gnPortID, 10); // wait for an ACK
- bResult = TRUE;
- }
- else
- bResult = FALSE; // transfer aborted
- _lclose(hFile);
- DestroyTransferDialog();
- return bResult; // success
- }
-
-
- // Download a file using the XModem protocol return TRUE if success
- // hParent: the handle of the window that should be the parent of
- // the transfer status dialog box.
- // gnPortID: the port ID of the port which is used for the transfer
- // szFilename: the name of the file to be transfered
- BOOL DownloadXModem(HWND hParent, int gnPortID, char *szFilename)
- {
- int nCurrentBlock=0, nSOH= 0, nBlock, nNotBlock, i, hFile;
- BOOL bCRC = TRUE, bFirst = TRUE;
- WORD wOurChecksum, wChecksumSent;
-
- hFile = _lcreat(szFilename, 0);
- if ((gnPortID < 0) || (hFile < 0))
- return FALSE; // Port is not open --> failure
- if (!CreateTransferDialog(hParent, "XMODEM Download (Checksum)", szFilename))
- return FALSE; // Couldn't create dialog --> failure
- if(!ghStatusDlg)
- return FALSE;
- // Send Cs then NAKs until the sender starts sending
- gcTries = 0;
- while (nSOH != SOH && gcTries < RETRIES)
- {
- bCRC = (gcTries++ < CRCTRIES);
- ComWriteChar(gnPortID, bCRC ? CRC : NAK);
- nSOH = ComReadCharTimeOut(gnPortID, 6);
- if (nSOH != -1 && nSOH != SOH)
- Sleep(6);
- }
- if (bCRC)
- SetWindowText(ghStatusDlg, "XMODEM Download (CRC)");
- while ((gcTries < RETRIES) && (!gbUserCanceled))
- {
- if (gbTimeOut)
- ReceiveError(errTIMEOUT, NAK);
- SetDlgItemInt(ghStatusDlg, IDD_BLOCK, nCurrentBlock + 1, FALSE);
- if (!bFirst)
- {
- nSOH = ComReadCharTimeOut(gnPortID, 10);
- if (nSOH == -1) // TimeOut
- continue;
- else if (nSOH == CAN)
- break;
- else if (nSOH == EOT)
- {
- ComWriteChar(gnPortID, ACK);
- break;
- }
- }
- bFirst = FALSE;
- nBlock = ComReadCharTimeOut(gnPortID, 1); // block number
- nNotBlock = ComReadCharTimeOut(gnPortID, 1); // 1's complement
- // get data block
- for (i = 0, wOurChecksum = 0 ; i < 128; i++)
- {
- int nValue;
- nValue = ComReadCharTimeOut(gnPortID,1);
- if (nValue < 0) // Time out?
- break;
- gachBuffer[i] = (char)nValue;
- wOurChecksum = (wOurChecksum + (*(gachBuffer + i)) & 255) & 255;
- }
- if (i != 128)
- continue;
- // checksum or crc from sender
- wChecksumSent = ComReadCharTimeOut(gnPortID, 1);
- if (bCRC)
- wChecksumSent = (wChecksumSent << 8) +
- ComReadCharTimeOut(gnPortID, 1);
- if (gbTimeOut)
- continue;
- if (nSOH != SOH) // Check the nSOH
- {
- ReceiveError(errINVALIDSOH, NAK);
- continue;
- }
- if (LOBYTE(nBlock) == LOBYTE(nCurrentBlock))
- _llseek(hFile, -128L, 1);
- else if (LOBYTE(nBlock) != LOBYTE(nCurrentBlock + 1) )
- {
- ComWriteChar(gnPortID, CAN);
- ReceiveError(errINVALIDBLOCK, CAN);
- break;
- }
- else
- nCurrentBlock++;
- // Test the block # 1s complement
- if (LOBYTE(nNotBlock) != LOBYTE(~nCurrentBlock))
- {
- ReceiveError(errINVALIDBLOCK, NAK);
- continue;
- }
- if (bCRC)
- wOurChecksum = CalculateCRC(gachBuffer, 130);
- // Test wOurChecksum or crc vs one sent
- if (wChecksumSent != wOurChecksum)
- {
- ReceiveError(errINVALIDCHECKSUM, NAK);
- continue;
- }
- nSOH = nBlock = nNotBlock = wChecksumSent = gcTries = 0;
- // Write the block to disk
- _lwrite(hFile, gachBuffer, 128);
- if (gbUserCanceled)
- {
- ComWriteChar(gnPortID, CAN);
- ComWriteChar(gnPortID, CAN);
- break;
- }
- ComWriteChar(gnPortID, ACK);
- }
- _lclose(hFile);
- DestroyTransferDialog();
- return (nSOH == EOT); // return TRUE if transfer was succesfull
- }
-
- static void SendError(int erno)
- {
- ++gcTries;
- SetDlgItemInt(ghStatusDlg, IDD_ERRORS, gcTries, FALSE);
- SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, aszError[erno]);
- }
-
- static void ReceiveError(int erno, int rtn)
- {
- ++gcTries;
- SetDlgItemInt(ghStatusDlg, IDD_ERRORS, gcTries, FALSE);
- SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, aszError[erno]);
- ComWriteChar(gnPortID, rtn);
- Sleep(18);
- FlushComm(gnPortID, 0);
- FlushComm(gnPortID, 1);
- }
-
- WORD CalculateCRC(char *pchBuffer, int nLen)
- {
- int i;
- DWORD dwCRC = 0;
-
- while (nLen--)
- {
- dwCRC |= (*pchBuffer++) & 255;
- for (i = 0; i < 8; i++)
- {
- dwCRC <<= 1;
- if (dwCRC & 0x1000000L)
- dwCRC ^= 0x102100L;
- }
- }
- return (WORD) (dwCRC >> 8);
- }
-